package com.ebs.md.fxmonitor.performance;

import org.apache.log4j.Logger;

import com.ebs.commons.util.DateTimeFormatter;
import com.ebs.commons.util.DateTimeFormatterFactory;

import com.ebs.commons.comp.IParsedPayload;

/**
 * <p>Title: NetLink</p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2007</p>
 *
 * <p>Company: </p>
 *
 * @author ygrinberg
 * @version 1.0
 */
public class ComponentTiming implements LogFormatter
{
    private static final String nsecPrecision = "nsec";
    private static final String msecPrecision = "msec";
    private static final String secPrecision = "sec";
    private static final String spaceValue = " ";

    DateTimeFormatter m_Formatter = DateTimeFormatterFactory.getInstance().getDateTimeFormatter();

    Logger s_logger = Logger.getLogger("netlink.latency");

    String m_blockLabel;



    String m_EntryKeyName;
    String m_entryLabel;
    String m_ExitKeyName;
    String m_exitLabel;
    String m_delimiter = spaceValue;
    int m_printCondition = 0;

    public void setBlockLabel(String theText)
    {
        m_blockLabel = theText;
    }

    public String getBlockLabel()
    {
        return m_blockLabel;
    }

    public void setEntryKeyName(String theName)
    {
        m_EntryKeyName = theName;
        if((m_EntryKeyName != null) && (m_EntryKeyName.length() > 0))
        {
            m_printCondition = (m_printCondition & 0xFFFFFFFB) + 4;
        }
        else
        {
            m_printCondition = (m_printCondition & 0xFFFFFFFB);
        }
    }

    public void setEntryLabel(String theLabel)
    {
        m_entryLabel = theLabel;
    }

    public void setExitKeyName(String theName)
    {
        m_ExitKeyName = theName;
        if((m_ExitKeyName != null) && (m_ExitKeyName.length() > 0))
        {
            m_printCondition = (m_printCondition & 0xFFFFFFF7) + 8;
        }
        else
        {
            m_printCondition = m_printCondition & 0xFFFFFFF7;
        }
    }

    public void setExitLabel(String theLabel)
    {
        m_exitLabel = theLabel;
    }

    public void setDelimiter(String theText)
    {
        m_delimiter = theText;
    }


    public void setPrecision(String thePrecision)
    {
        if(thePrecision.equalsIgnoreCase(nsecPrecision))
        {
           m_printCondition = (m_printCondition & 0xFFFFFFFC) + 1;
           return;
        }

        if(thePrecision.equalsIgnoreCase(msecPrecision))
        {
            m_printCondition = (m_printCondition & 0xFFFFFFFC) + 2;
            return;
        }

        if(thePrecision.equalsIgnoreCase(secPrecision))
        {
            m_printCondition = (m_printCondition & 0xFFFFFFFC) + 3;
            return;
        }

        throw new IllegalArgumentException("Precision " + thePrecision + " is not a valid value. Specify nsec, msec or sec");
    }


    public void format(IParsedPayload thePayload,
                                            StringBuilder theContents)
    {
        if((theContents == null)||(thePayload == null))
        {
            return;
        }

        if((m_blockLabel != null) && (m_blockLabel.length()>0))
        {
            theContents.append(m_blockLabel);
            theContents.append(spaceValue);
        }
        addDateTime(thePayload, theContents);
    }

    public void addDateTime(IParsedPayload thePayload,
                            StringBuilder theContents)
    {
        switch (m_printCondition)
        {
           case 0:
           case 1:
           case 2:
           case 3:
               /* Precision without keys.
                  In this case we just print labels if they exists
               */
               addLabel(m_entryLabel, theContents);
               addLabel(m_exitLabel, theContents);
               break;
           case 4:
               /* Entry key, no precision, print entry value, not Date
               */
              addValue(m_entryLabel, m_EntryKeyName, thePayload, theContents);
              break;
           case 5:
           case 6:
           case 7:
               /* Only entry key has been provided
               */
              addBlock(m_entryLabel, m_EntryKeyName, thePayload, theContents);
              break;
          case 8:
              /* Exit key, no precision, print exit value, not Date
              */
             addValue(m_exitLabel, m_ExitKeyName, thePayload, theContents);
             break;
          case 9:
          case 10:
          case 11:
              /* Only exit key has been provided
               */
              addBlock(m_exitLabel, m_ExitKeyName, thePayload, theContents);
              break;
          case 12:
              /* Both keys have been provided no precision. Print values, not Date
               */
              addValue(m_entryLabel, m_EntryKeyName, thePayload, theContents);
              addValue(m_exitLabel, m_ExitKeyName, thePayload, theContents);
              break;
          case 13:
          case 14:
          case 15:
              /* Both keys have been provided with precision
               */
              addBlock(m_entryLabel, m_EntryKeyName, thePayload, theContents);
              addBlock(m_exitLabel, m_ExitKeyName, thePayload, theContents);
              addDuration(thePayload, theContents);
              break;
           default:
               break;
        }
    }

    public void addValue(String theLabel,
                         String theKeyName,
                         IParsedPayload thePayload,
                         StringBuilder theContents)
    {
        if((theLabel != null) && (theLabel.length() > 0))
        {
            addLabel(theLabel, theContents);
            Object theValue = thePayload.getParsedMessage().get(theKeyName);
            if(theValue != null)
            {
                theContents.append(theValue.toString());
            }
            theContents.append(m_delimiter);
        }
    }


    public void addLabel(String theLabel, StringBuilder theContents)
    {
        if((theLabel != null)&&(theLabel.length() > 0))
        {
            theContents.append(theLabel);
            theContents.append(spaceValue);
        }
    }


    public void addBlock(String theLabel,
                         String theKeyName,
                         IParsedPayload thePayload,
                         StringBuilder theContents)
    {
        if((theLabel != null) && (theLabel.length() > 0))
        {
            addLabel(theLabel, theContents);
            long theNumericTime = getTimeMillis(thePayload, theKeyName);
            theContents.append(m_Formatter.formatTimeStampFromMillis(
                    theNumericTime));
            theContents.append(m_delimiter);
        }
    }

    public void addDuration(IParsedPayload thePayload, StringBuilder theContents)
    {
        long theBegin = getTimeMillis(thePayload, m_EntryKeyName);
        long theEnd = getTimeMillis(thePayload, m_ExitKeyName);
        theContents.append(theEnd - theBegin);
        theContents.append(spaceValue);
        int precision = m_printCondition & 0x00000003;
        switch(precision){
           case 1:
               theContents.append(nsecPrecision);
               break;
           case 2:
               theContents.append(msecPrecision);
               break;
           case 3:
               theContents.append(secPrecision);
               break;
        }
        theContents.append(m_delimiter);
    }


    private long getTimeMillis(IParsedPayload thePayload, String theKey)
    {
        int precision = m_printCondition & 0x00000003;
        long toReturn = 0;
        switch(precision){
           case 1:
               toReturn = (thePayload.getParsedLongField(theKey))/1000000;
               break;
           case 2:
               toReturn = thePayload.getParsedLongField(theKey);
               break;
           case 3:
               toReturn = (thePayload.getParsedIntField(theKey))*1000L;
               break;
           default:
               break;
        }
        return toReturn;
    }
}
